// Package main cert-deploy-apache 主程序
package main

import (
    "context"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "encoding/json"
    "path/filepath"
    "crypto/sha256"
    "encoding/hex"
    "log/slog"
    "os"
    "strings"
    "time"

	"github.com/cnssl/cert-deploy/internal/backup"
	"github.com/cnssl/cert-deploy/internal/config"
	"github.com/cnssl/cert-deploy/internal/daemon"
    "github.com/cnssl/cert-deploy/internal/deployer"
    "github.com/cnssl/cert-deploy/internal/csr"
	"github.com/cnssl/cert-deploy/internal/fetcher"
	"github.com/cnssl/cert-deploy/internal/scanner"
    "github.com/cnssl/cert-deploy/internal/validator"
    "github.com/cnssl/cert-deploy/internal/util"
	"github.com/spf13/cobra"
)

var (
    // 全局变量
    cfgManager *config.Manager
    logger     *slog.Logger

	// 命令行标志
	siteName string
    referID  string
    apiURL   string
    force    bool

    // 扫描配置
    apacheDirs string // 逗号分隔的自定义 Apache 配置目录

    // 私钥/CSR 生成参数
    keyType  string
    keySize  int
    keyCurve string
    csrOrg   string
    // csrOU removed per spec
    csrC     string
    csrST    string
    csrL     string
    csrEmail string
    valMethod string
)

// 通过 ldflags 注入版本号，例如：-ldflags "-X main.version=v2.0.1"
var version = "dev"

func main() {
	// 获取可执行文件路径
	execPath, err := os.Executable()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get executable path: %v\n", err)
		os.Exit(1)
	}

	// 初始化配置管理器
	cfgManager, err = config.NewManager(execPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to initialize config manager: %v\n", err)
		os.Exit(1)
	}

	// 初始化日志
	logger = setupLogger()

	// 创建根命令
	rootCmd := &cobra.Command{
		Use:   "cert-deploy-apache",
		Short: "Automatic certificate deployment tool for Apache",
		Long:  "A lightweight tool to automatically deploy certificates from API to Apache servers",
	}

	// 添加子命令
	rootCmd.AddCommand(scanCmd())
	rootCmd.AddCommand(initCmd())
	rootCmd.AddCommand(deployCmd())
	rootCmd.AddCommand(statusCmd())
	rootCmd.AddCommand(checkCmd())
	rootCmd.AddCommand(daemonCmd())
    rootCmd.AddCommand(versionCmd())
    rootCmd.AddCommand(configCmd())
    rootCmd.AddCommand(autoCmd())

	// 执行命令
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

// initCmd 初始化站点配置命令
func initCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "init",
		Short: "Initialize site configuration",
		Long:  "Create a new site configuration file with the specified settings",
		RunE:  runInit,
	}

	cmd.Flags().StringVar(&siteName, "site", "", "Site name (required)")
	cmd.Flags().StringVar(&referID, "refer-id", "", "API refer ID (required)")
	cmd.Flags().StringVar(&apiURL, "api-url", "", "API URL (required)")
    // Key/CSR flags
    cmd.Flags().StringVar(&keyType, "key-type", "rsa", "Key type: rsa|ecdsa (default rsa)")
    cmd.Flags().IntVar(&keySize, "key-size", 2048, "RSA key size: 2048|4096 (default 2048)")
    cmd.Flags().StringVar(&keyCurve, "key-curve", "prime256v1", "ECDSA curve: prime256v1|secp384r1|secp521r1")
    cmd.Flags().StringVar(&csrOrg, "csr-org", "", "CSR Organization (O)")
    cmd.Flags().StringVar(&csrC, "csr-c", "", "CSR Country (C)")
    cmd.Flags().StringVar(&csrST, "csr-st", "", "CSR State/Province (ST)")
    cmd.Flags().StringVar(&csrL, "csr-l", "", "CSR Locality/City (L)")
    cmd.Flags().StringVar(&csrEmail, "csr-email", "", "CSR Email")

    cmd.MarkFlagRequired("site")
	cmd.MarkFlagRequired("refer-id")
	cmd.MarkFlagRequired("api-url")

	return cmd
}

// deployCmd 部署证书命令
func deployCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "deploy",
		Short: "Deploy certificate to site",
		Long:  "Fetch certificate from API and deploy to the specified site",
		RunE:  runDeploy,
	}

	cmd.Flags().StringVar(&siteName, "site", "", "Site name (required)")
	cmd.Flags().BoolVar(&force, "force", false, "Force deployment even if certificate is not expiring")
	cmd.MarkFlagRequired("site")

	return cmd
}

// statusCmd 查看站点状态命令
func statusCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "status",
		Short: "Show status of all sites",
		Long:  "Display the status of all configured sites",
		RunE:  runStatus,
	}

	return cmd
}

// checkCmd 检查证书状态命令
func checkCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "check",
		Short: "Check certificate expiration",
		Long:  "Check if certificates need renewal",
		RunE:  runCheck,
	}

	cmd.Flags().StringVar(&siteName, "site", "", "Site name (check specific site)")

	return cmd
}

// scanCmd 扫描配置命令
func scanCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "scan",
        Short: "Scan Apache configuration",
        Long:  "Scan Apache configuration files and discover SSL sites",
        RunE:  runScan,
    }

    cmd.Flags().StringVar(&apacheDirs, "apache-dirs", "", "Comma-separated Apache config directories (override defaults)")

    return cmd
}

// daemonCmd 守护进程命令
func daemonCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "daemon",
		Short: "Run in daemon mode",
		Long:  "Run in background and automatically check/deploy certificates",
		RunE:  runDaemon,
	}

	return cmd
}

// versionCmd 版本命令
func versionCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "version",
        Short: "Show version information",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("cert-deploy-apache %s\n", version)
        },
    }

    return cmd
}

// config 命令
func configCmd() *cobra.Command { cmd := &cobra.Command{Use: "config", Short: "Manage site config"}; cmd.AddCommand(configSetCmd()); return cmd }
func configSetCmd() *cobra.Command {
    var site string
    var setKeyType string
    var setKeySize int
    var setKeyCurve string
    var setOrg, setC, setST, setL, setEmail string
    c := &cobra.Command{ Use: "set", Short: "Set key/csr options for a site",
        RunE: func(cmd *cobra.Command, args []string) error {
            if site == "" { return fmt.Errorf("--site is required") }
            sc, err := cfgManager.LoadSite(site); if err != nil { return fmt.Errorf("load site failed: %w", err) }
            if setKeyType != "" { sc.Key.Type = setKeyType }
            if setKeySize != 0 { sc.Key.Size = setKeySize }
            if setKeyCurve != "" { sc.Key.Curve = setKeyCurve }
            if setOrg != "" { sc.CSR.Organization = setOrg }
            if setC != "" { sc.CSR.Country = setC }
            if setST != "" { sc.CSR.State = setST }
            if setL != "" { sc.CSR.Locality = setL }
            if setEmail != "" { sc.CSR.Email = setEmail }
            if err := cfgManager.SaveSite(sc); err != nil { return fmt.Errorf("save site failed: %w", err) }
            fmt.Println("✓ Config updated for", site); return nil }, }
    c.Flags().StringVar(&site, "site", "", "Site name (required)")
    c.Flags().StringVar(&setKeyType, "key-type", "", "rsa|ecdsa")
    c.Flags().IntVar(&setKeySize, "key-size", 0, "RSA key size: 2048|4096")
    c.Flags().StringVar(&setKeyCurve, "key-curve", "", "ECDSA curve")
    c.Flags().StringVar(&setOrg, "csr-org", "", "CSR Organization (O)")
    c.Flags().StringVar(&setC, "csr-c", "", "CSR Country (C)")
    c.Flags().StringVar(&setST, "csr-st", "", "CSR State/Province (ST)")
    c.Flags().StringVar(&setL, "csr-l", "", "CSR Locality/City (L)")
    c.Flags().StringVar(&setEmail, "csr-email", "", "CSR Email")
    return c }

// autoCmd 一键向导
func autoCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "auto",
        Short: "Interactive setup wizard",
        RunE:  runAuto,
    }
    cmd.Flags().StringVar(&apacheDirs, "apache-dirs", "", "Comma-separated Apache config directories (override defaults)")
    cmd.Flags().StringVar(&apiURL, "api-url", "", "API URL (required if not found in configs)")
    // defaults for new sites
    cmd.Flags().StringVar(&keyType, "key-type", "rsa", "Key type: rsa|ecdsa (default rsa)")
    cmd.Flags().IntVar(&keySize, "key-size", 2048, "RSA key size: 2048|4096 (default 2048)")
    cmd.Flags().StringVar(&keyCurve, "key-curve", "prime256v1", "ECDSA curve: prime256v1|secp384r1|secp521r1")
    cmd.Flags().StringVar(&csrOrg, "csr-org", "", "CSR Organization (O)")
    cmd.Flags().StringVar(&csrC, "csr-c", "", "CSR Country (C)")
    cmd.Flags().StringVar(&csrST, "csr-st", "", "CSR State/Province (ST)")
    cmd.Flags().StringVar(&csrL, "csr-l", "", "CSR Locality/City (L)")
    cmd.Flags().StringVar(&csrEmail, "csr-email", "", "CSR Email")
    cmd.Flags().StringVar(&valMethod, "validation-method", "", "Validation method: txt|file|admin|administrator|postmaster|webmaster|hostmaster")
    return cmd
}

// runAuto 执行一键向导
func runAuto(cmd *cobra.Command, args []string) error {
    fmt.Println("→ Welcome to cert-deploy-apache interactive setup")

    // 1) 扫描 Apache 站点
    var s *scanner.ApacheScanner
    if strings.TrimSpace(apacheDirs) != "" {
        var dirs []string
        for _, p := range strings.Split(apacheDirs, ",") {
            p = strings.TrimSpace(p)
            if p != "" { dirs = append(dirs, p) }
        }
        s = scanner.NewApacheScannerWithDirs(dirs)
    } else {
        s = scanner.NewApacheScanner()
    }
    sites, err := s.Scan()
    if err != nil { return fmt.Errorf("scan failed: %w", err) }
    if len(sites) == 0 { fmt.Println("✗ No SSL sites found. You can still initialize configs.") } else { fmt.Printf("✓ Found %d SSL site(s)\n", len(sites)) }

    // 2) refer_id
    referIDs := readReferIDs()
    if len(referIDs) == 0 {
        old, _ := cfgManager.ListSites()
        for _, sc := range old { if sc.API.ReferID != "" { referIDs = append(referIDs, sc.API.ReferID) } }
        if len(referIDs) > 0 && !promptYesNo(fmt.Sprintf("Use %d refer_id(s) from existing configs?", len(referIDs)), true) {
            referIDs = nil
        }
    }
    if len(referIDs) == 0 { return fmt.Errorf("no refer_id provided. Please provide at least one") }

    // 3) API URL
    api := strings.TrimSpace(apiURL)
    if api == "" {
        if old, _ := cfgManager.ListSites(); len(old) > 0 && old[0].API.URL != "" { api = old[0].API.URL }
    }
    if api == "" { fmt.Print("Enter API URL: "); api = strings.TrimSpace(readLine()); if api == "" { return fmt.Errorf("API URL is required") } }

    // 4) 处理已有配置
    existing, _ := cfgManager.ListSites()
    if len(existing) > 0 {
        modified := false
        for _, sc := range existing { if !validateConfigHashApache(sc) { modified = true; logger.Warn("site config modified", "site", sc.SiteName) } }
        fmt.Printf("Found %d existing site config(s).\n", len(existing))
        if modified { fmt.Println("Some configs seem modified manually.") }
        if !promptYesNo("Use existing configs? (Y=keep, N=backup and regenerate)", true) {
            if err := backupAndResetSitesApache(); err != nil { return err }
            existing = nil
        }
    }

    // 5) 生成站点配置
    idx := 0
    for _, si := range sites {
        rid := ""; if idx < len(referIDs) { rid = referIDs[idx] }; idx++
        siteCfg := &config.SiteConfig{
            Version:    "1.0",
            SiteName:   si.Name,
            Enabled:    rid != "",
            ServerType: "apache",
            API:        config.APIConfig{URL: api, ReferID: rid},
            Domains:    si.Domains,
            Paths: config.PathsConfig{
                Certificate: si.CertPath,
                PrivateKey:  si.KeyPath,
                ConfigFile:  si.ConfigFile,
                ChainFile:   "",
            },
            Reload:     config.ReloadConfig{TestCommand: "apachectl configtest", ReloadCommand: "systemctl reload apache2"},
            Schedule:   config.ScheduleConfig{CheckIntervalHours: 12, RenewBeforeDays: 15},
            Validation: config.ValidationConfig{VerifyDomain: true, TestHTTPS: false, TestURL: fmt.Sprintf("https://%s", si.Name)},
            Backup:     config.BackupConfig{Enabled: true, KeepVersions: 3},
            Metadata:   config.MetadataConfig{CreatedAt: time.Now(), AutoScanned: true},
        }
        if err := cfgManager.SaveSite(siteCfg); err != nil { return fmt.Errorf("save site failed: %w", err) }
            if err := setSiteConfigHashApache(siteCfg); err != nil { logger.Warn("failed to set config hash", "site", siteCfg.SiteName, "error", err) }
            if rid != "" {
                if err := startIssuanceApache(siteCfg); err != nil { logger.Warn("issuance start failed", "site", siteCfg.SiteName, "error", err) }
            }
    }

    // 6) 写入 SSL 模板
    if err := ensureTemplatesApache(); err != nil { logger.Warn("failed to write templates", "error", err) }

    fmt.Println("\n✓ Setup completed.")
    fmt.Println("- Edit configs under:", cfgManager.GetSitesDir())
    fmt.Println("- Start daemon: cert-deploy-apache daemon")
    return nil
}

// 交互与校验辅助
func readReferIDs() []string { fmt.Println("Enter refer_id (one per line), blank line to finish:"); var ids []string; for { line := readLine(); if strings.TrimSpace(line) == "" { break }; ids = append(ids, strings.TrimSpace(line)) }; return ids }
func readLine() string { var s string; fmt.Print("> "); fmt.Scanln(&s); return s }
func promptYesNo(prompt string, defYes bool) bool { def := "Y"; if !defYes { def = "N" }; fmt.Printf("%s [%s]: ", prompt, def); var in string; fmt.Scanln(&in); in = strings.TrimSpace(strings.ToLower(in)); if in == "" { return defYes }; return in == "y" || in == "yes" }
func validateConfigHashApache(sc *config.SiteConfig) bool { cp := *sc; cp.Metadata.ConfigHash = ""; data, _ := json.Marshal(cp); sum := sha256.Sum256(data); got := hex.EncodeToString(sum[:]); return got == sc.Metadata.ConfigHash && got != "" }
func setSiteConfigHashApache(sc *config.SiteConfig) error { cp := *sc; cp.Metadata.ConfigHash = ""; data, err := json.Marshal(cp); if err != nil { return err }; sum := sha256.Sum256(data); sc.Metadata.ConfigHash = hex.EncodeToString(sum[:]); return cfgManager.SaveSite(sc) }
func backupAndResetSitesApache() error { src := cfgManager.GetSitesDir(); ts := time.Now().Format("20060102-150405"); dst := filepath.Join(cfgManager.GetWorkDir(), "sites.bak-"+ts); if err := os.Rename(src, dst); err != nil { return fmt.Errorf("backup sites failed: %w", err) }; return os.MkdirAll(src, 0755) }
func ensureTemplatesApache() error { tplDir := filepath.Join(cfgManager.GetWorkDir(), "templates"); if err := os.MkdirAll(tplDir, 0755); err != nil { return err }; apacheTpl := `<VirtualHost *:443>
    ServerName {{ .ServerName }}
    # Copy common settings from your port 80 vhost
    SSLEngine on
    SSLCertificateFile {{ .Certificate }}
    SSLCertificateKeyFile {{ .PrivateKey }}
    # SSLCertificateChainFile {{ .ChainFile }}
</VirtualHost>
`; return os.WriteFile(filepath.Join(tplDir, "apache_ssl.tmpl"), []byte(apacheTpl), 0644) }

// startIssuanceApache 生成CSR并发起签发
func startIssuanceApache(sc *config.SiteConfig) error {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    f := fetcher.New(30 * time.Second)
    info, _ := f.Info(ctx, sc.API.URL, sc.API.ReferID)
    cn := sc.CSR.CommonName
    if cn == "" { if info != nil && info.CommonName != "" { cn = info.CommonName } else if len(sc.Domains) > 0 { cn = sc.Domains[0] } else { cn = sc.SiteName } }
    keyPEM, csrPEM, _, err := csr.GenerateKeyAndCSR(csr.KeyOptions{Type: sc.Key.Type, Size: sc.Key.Size, Curve: sc.Key.Curve}, csr.CSROptions{CommonName: cn, Organization: sc.CSR.Organization, Country: sc.CSR.Country, State: sc.CSR.State, Locality: sc.CSR.Locality, Email: sc.CSR.Email})
    if err != nil { return err }
    if err := os.MkdirAll(filepath.Dir(sc.Paths.PrivateKey), 0755); err == nil { if werr := os.WriteFile(sc.Paths.PrivateKey, []byte(keyPEM), 0600); werr != nil { return werr } }
    logger.Info("start issuance", "site", sc.SiteName, "refer", util.MaskReferID(sc.API.ReferID), "method", sc.Validation.Method)
    _, err = f.StartOrUpdate(ctx, sc.API.URL, sc.API.ReferID, csrPEM, cn, sc.Validation.Method)
    return err
}

// runInit 执行初始化
func runInit(cmd *cobra.Command, args []string) error {
	logger.Info("initializing site configuration", "site", siteName)

	// 检查站点是否已存在
	if cfgManager.SiteExists(siteName) {
		return fmt.Errorf("site configuration already exists: %s", siteName)
	}

	// 创建默认配置
	siteConfig := &config.SiteConfig{
		Version:    "1.0",
		SiteName:   siteName,
		Enabled:    true,
		ServerType: "apache",
		API: config.APIConfig{
			URL:     apiURL,
			ReferID: referID,
		},
		Domains: []string{siteName},
        Paths: config.PathsConfig{
            Certificate: fmt.Sprintf("/etc/apache2/ssl/%s/cert.pem", siteName),
            PrivateKey:  fmt.Sprintf("/etc/apache2/ssl/%s/key.pem", siteName),
            ChainFile:   fmt.Sprintf("/etc/apache2/ssl/%s/chain.pem", siteName),
            ConfigFile:  fmt.Sprintf("/etc/apache2/sites-enabled/%s.conf", siteName),
        },
		Reload: config.ReloadConfig{
			TestCommand:   "apachectl configtest",
			ReloadCommand: "systemctl reload apache2",
		},
		Schedule: config.ScheduleConfig{
			CheckIntervalHours: 12,
			RenewBeforeDays:    15,
		},
        Validation: config.ValidationConfig{VerifyDomain: true, TestHTTPS: false, TestURL: fmt.Sprintf("https://%s", siteName), IgnoreDomainMismatch: false, Method: valMethod},
		Backup: config.BackupConfig{
			Enabled:      true,
			KeepVersions: 3,
		},
        Metadata: config.MetadataConfig{
            CreatedAt:   time.Now(),
            AutoScanned: false,
        },
        Key: config.KeyConfig{Type: keyType, Size: keySize, Curve: keyCurve},
        CSR: config.CSRConfig{Organization: csrOrg, Country: csrC, State: csrST, Locality: csrL, Email: csrEmail},
    }

	// 保存配置
	if err := cfgManager.SaveSite(siteConfig); err != nil {
		return fmt.Errorf("failed to save configuration: %w", err)
	}

	logger.Info("site configuration created",
		"site", siteName,
		"config_file", cfgManager.GetSitesDir()+"/"+siteName+".json")

    fmt.Printf("✓ Created configuration for %s\n", siteName)
    fmt.Printf("  Key: type=%s", keyType)
    if keyType == "rsa" { fmt.Printf(", size=%d", keySize) } else { fmt.Printf(", curve=%s", keyCurve) }
    fmt.Printf("\n")
    if csrOrg != "" || csrC != "" || csrST != "" || csrL != "" || csrEmail != "" {
        fmt.Printf("  CSR: O=%s, C=%s, ST=%s, L=%s, Email=%s\n", csrOrg, csrC, csrST, csrL, csrEmail)
    }
	fmt.Printf("  Config file: %s/%s.json\n", cfgManager.GetSitesDir(), siteName)
	fmt.Printf("\nNext steps:\n")
	fmt.Printf("  1. Edit the configuration file to match your setup\n")
	fmt.Printf("  2. Run 'cert-deploy-apache deploy --site %s' to deploy certificate\n", siteName)

	return nil
}

// runDeploy 执行部署
func runDeploy(cmd *cobra.Command, args []string) error {
	logger.Info("starting deployment", "site", siteName)

	// 1. 加载站点配置
	siteConfig, err := cfgManager.LoadSite(siteName)
	if err != nil {
		return fmt.Errorf("failed to load site configuration: %w", err)
	}

	if !siteConfig.Enabled {
		logger.Info("site is disabled, skipping", "site", siteName)
		return nil
	}

	// 2. 检查是否需要续期
	if !force && !siteConfig.NeedsRenewal() {
		days := siteConfig.DaysUntilExpiry()
		logger.Info("certificate does not need renewal",
			"site", siteName,
			"days_until_expiry", days)
		fmt.Printf("✓ Certificate for %s is valid for %d more days\n", siteName, days)
		return nil
	}

	// 3. 从 API 获取证书
	logger.Info("fetching certificate from API", "site", siteName)
	fmt.Printf("→ Fetching certificate from API...\n")

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	f := fetcher.New(30 * time.Second)
	certData, err := f.Info(ctx, siteConfig.API.URL, siteConfig.API.ReferID)
	if err != nil {
		return fmt.Errorf("failed to fetch certificate: %w", err)
	}

	// 检查证书状态
	if certData.Status != "active" {
		logger.Warn("certificate not active",
			"site", siteName,
			"status", certData.Status)
		fmt.Printf("⚠ Certificate status: %s (not active, will retry later)\n", certData.Status)

		// 更新重试时间
		siteConfig.Metadata.LastCheckAt = time.Now()
		cfgManager.SaveSite(siteConfig)
		return nil
	}

	logger.Info("certificate fetched", "site", siteName, "status", certData.Status)
	fmt.Printf("✓ Certificate fetched successfully\n")

	// 4. 验证证书
	fmt.Printf("→ Validating certificate...\n")

	// 解析证书
	block, _ := pem.Decode([]byte(certData.Cert))
	if block == nil {
		return fmt.Errorf("failed to decode certificate PEM")
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse certificate: %w", err)
	}

	// 验证域名覆盖
	domainValidator := validator.NewDomainValidator(
		siteConfig.Domains,
		siteConfig.Validation.IgnoreDomainMismatch,
	)

	if err := domainValidator.ValidateDomainCoverage(cert); err != nil {
		return fmt.Errorf("domain validation failed: %w", err)
	}

	logger.Info("certificate validated",
		"site", siteName,
		"subject", cert.Subject.CommonName,
		"expires_at", cert.NotAfter)
	fmt.Printf("✓ Certificate validated\n")

	// 5. 备份旧证书
	if siteConfig.Backup.Enabled {
		fmt.Printf("→ Backing up old certificate...\n")

		backupManager := backup.NewManager(
			cfgManager.GetBackupDir(),
			siteConfig.Backup.KeepVersions,
		)

		certInfo := &backup.CertInfo{
			Subject:   cert.Subject.CommonName,
			Serial:    fmt.Sprintf("%X", cert.SerialNumber),
			NotBefore: cert.NotBefore,
			NotAfter:  cert.NotAfter,
		}

		backupPath, err := backupManager.Backup(
			siteName,
			siteConfig.Paths.Certificate,
			siteConfig.Paths.PrivateKey,
			certInfo,
		)

		if err != nil {
			logger.Warn("backup failed", "error", err)
			fmt.Printf("⚠ Backup failed: %v\n", err)
		} else {
			logger.Info("backup created", "path", backupPath)
			fmt.Printf("✓ Backup created: %s\n", backupPath)
		}
	}

	// 6. 部署证书
	fmt.Printf("→ Deploying certificate...\n")

	apacheDeployer := deployer.NewApacheDeployer(
		siteConfig.Paths.Certificate,
		siteConfig.Paths.PrivateKey,
		siteConfig.Paths.ChainFile,
		siteConfig.Reload.TestCommand,
		siteConfig.Reload.ReloadCommand,
	)

	keyContent, err := os.ReadFile(siteConfig.Paths.PrivateKey)
	if err != nil { return fmt.Errorf("read private key failed: %w", err) }
	if err := apacheDeployer.Deploy(certData.Cert, certData.IntermediateCert, string(keyContent)); err != nil {
		return fmt.Errorf("deployment failed: %w", err)
	}

	logger.Info("certificate deployed", "site", siteName)
	fmt.Printf("✓ Certificate deployed successfully\n")

	// 7. 更新元数据
	siteConfig.Metadata.LastDeployAt = time.Now()
	siteConfig.Metadata.LastCheckAt = time.Now()
	siteConfig.Metadata.CertExpiresAt = cert.NotAfter
	siteConfig.Metadata.CertSerial = fmt.Sprintf("%X", cert.SerialNumber)

	if err := cfgManager.SaveSite(siteConfig); err != nil {
		logger.Warn("failed to update metadata", "error", err)
	}

	fmt.Printf("\n✓ Deployment completed for %s\n", siteName)
	fmt.Printf("  Certificate expires: %s\n", cert.NotAfter.Format("2006-01-02 15:04:05"))
	fmt.Printf("  Days until expiry: %d\n", siteConfig.DaysUntilExpiry())

	return nil
}

// runStatus 执行状态查看
func runStatus(cmd *cobra.Command, args []string) error {
	sites, err := cfgManager.ListSites()
	if err != nil {
		return fmt.Errorf("failed to list sites: %w", err)
	}

	if len(sites) == 0 {
		fmt.Println("No sites configured")
		return nil
	}

	fmt.Printf("Sites: %d\n\n", len(sites))
	fmt.Println("┌────────────────────┬─────────┬─────────────────────┬──────────────┐")
	fmt.Println("│ Site               │ Enabled │ Expires At          │ Days Left    │")
	fmt.Println("├────────────────────┼─────────┼─────────────────────┼──────────────┤")

	for _, site := range sites {
		enabled := "✓"
		if !site.Enabled {
			enabled = "✗"
		}

		expiresAt := "N/A"
		daysLeft := "N/A"
		if !site.Metadata.CertExpiresAt.IsZero() {
			expiresAt = site.Metadata.CertExpiresAt.Format("2006-01-02 15:04")
			days := site.DaysUntilExpiry()
			daysLeft = fmt.Sprintf("%d", days)

			if days <= 7 {
				daysLeft += " ⚠️"
			}
		}

		fmt.Printf("│ %-18s │ %-7s │ %-19s │ %-12s │\n",
			truncate(site.SiteName, 18),
			enabled,
			expiresAt,
			daysLeft,
		)
	}

	fmt.Println("└────────────────────┴─────────┴─────────────────────┴──────────────┘")

	return nil
}

// runCheck 执行检查
func runCheck(cmd *cobra.Command, args []string) error {
	var sites []*config.SiteConfig
	var err error

	if siteName != "" {
		// 检查单个站点
		site, err := cfgManager.LoadSite(siteName)
		if err != nil {
			return fmt.Errorf("failed to load site: %w", err)
		}
		sites = []*config.SiteConfig{site}
	} else {
		// 检查所有站点
		sites, err = cfgManager.ListSites()
		if err != nil {
			return fmt.Errorf("failed to list sites: %w", err)
		}
	}

	needsRenewal := 0
	for _, site := range sites {
		if !site.Enabled {
			continue
		}

		if site.NeedsRenewal() {
			needsRenewal++
			days := site.DaysUntilExpiry()
			fmt.Printf("⚠ %s needs renewal (expires in %d days)\n", site.SiteName, days)
		} else {
			days := site.DaysUntilExpiry()
			fmt.Printf("✓ %s is OK (expires in %d days)\n", site.SiteName, days)
		}
	}

	if needsRenewal > 0 {
		fmt.Printf("\n%d site(s) need renewal\n", needsRenewal)
	} else {
		fmt.Println("\nAll certificates are valid")
	}

	return nil
}

// setupLogger 初始化日志
func setupLogger() *slog.Logger {
	opts := &slog.HandlerOptions{
		Level: slog.LevelInfo,
	}

	handler := slog.NewJSONHandler(os.Stdout, opts)
	return slog.New(handler)
}

// runDaemon 执行守护进程
func runDaemon(cmd *cobra.Command, args []string) error {
	logger.Info("starting daemon mode")
	fmt.Println("→ Starting daemon mode...")

	// 创建守护进程
	d := daemon.New(cfgManager, logger)

	// 运行守护进程
	fmt.Println("✓ Daemon started")
	fmt.Println("  Press Ctrl+C to stop")

	return d.Run()
}

// runScan 执行扫描
func runScan(cmd *cobra.Command, args []string) error {
    logger.Info("scanning apache configuration")
    fmt.Println("→ Scanning Apache configuration...")

    // 创建扫描器
    var apacheScanner *scanner.ApacheScanner
    if strings.TrimSpace(apacheDirs) != "" {
        parts := strings.Split(apacheDirs, ",")
        var cleaned []string
        for _, p := range parts {
            s := strings.TrimSpace(p)
            if s != "" {
                cleaned = append(cleaned, s)
            }
        }
        apacheScanner = scanner.NewApacheScannerWithDirs(cleaned)
    } else {
        apacheScanner = scanner.NewApacheScanner()
    }

	// 执行扫描
	sites, err := apacheScanner.Scan()
	if err != nil {
		return fmt.Errorf("failed to scan: %w", err)
	}

	if len(sites) == 0 {
		fmt.Println("✗ No SSL sites found")
		return nil
	}

	logger.Info("scan completed", "sites_found", len(sites))
	fmt.Printf("✓ Found %d SSL site(s)\n\n", len(sites))

	// 显示扫描结果
	fmt.Println("┌────────────────────┬──────────────────────┬──────────────┬────────────────┐")
	fmt.Println("│ Site               │ Cert Path            │ Expires      │ Days Left      │")
	fmt.Println("├────────────────────┼──────────────────────┼──────────────┼────────────────┤")

	for _, site := range sites {
		expiresStr := "N/A"
		daysLeftStr := "N/A"

		if !site.CurrentExpiry.IsZero() {
			expiresStr = site.CurrentExpiry.Format("2006-01-02")
			daysLeft := int(time.Until(site.CurrentExpiry).Hours() / 24)
			daysLeftStr = fmt.Sprintf("%d", daysLeft)

			if daysLeft <= 7 {
				daysLeftStr += " ⚠️"
			}
		}

		fmt.Printf("│ %-18s │ %-20s │ %-12s │ %-14s │\n",
			truncate(site.Name, 18),
			truncate(site.CertPath, 20),
			expiresStr,
			daysLeftStr,
		)

		// 显示域名
		if len(site.Domains) > 1 {
			fmt.Printf("│   Domains: %-62s │\n",
				truncate(strings.Join(site.Domains, ", "), 62))
		}
	}

	fmt.Println("└────────────────────┴──────────────────────┴──────────────┴────────────────┘")

	// 提示下一步操作
	fmt.Println("\nNext steps:")
	fmt.Println("  1. Use 'init' command to create site configurations")
	fmt.Println("  2. Or use 'auto' command to automatically configure and deploy")

	return nil
}

// truncate 截断字符串
func truncate(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen-3] + "..."
}
